package com.infinitus.activity.service.impl;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.infinitus.activity.actenum.QualiStateEnum;
import com.infinitus.activity.common.ResponseCodeConstant;
import com.infinitus.activity.pojo.DO.AuldrawVo;
import com.infinitus.activity.pojo.VO.*;
import com.infinitus.activity.service.DistributionLockService;
import com.infinitus.activity.util.*;
import com.infinitus.activity.web.LotteryController;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@Service
public class LotteryServiceImpl implements LotteryService {

    private Logger logger = LoggerFactory.getLogger(LotteryController.class);

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private RedisTemplate<String, String> stringRedisTemplate;

    @Autowired
    private DistributionLockService distributionLockService;

    @Autowired
    private ActivityConfigVO activityConfig;


    @Value("${deploycolId}")
    private String deploycolId;     //活动部署FkId

    @Value("${qualiDefNo}")
    private String qualiDefNo;    //资格自定义编码,唯一

    public boolean checkCanLottery(String dealerNo, String ruleType) {
        //每天可以抽取
        if (ruleType.equals("2")) {
            Map<Object, Object> stringMap = stringRedisTemplate.opsForHash().entries(KeyGenerator.getActivityListWinningBackup(deploycolId, dealerNo));
            List<AuldrawVo> auldrawVos = new ArrayList();
            for (Map.Entry<Object, Object> entry : stringMap.entrySet()) {
                AuldrawVo auldrawVo = JSONObject.parseObject(String.valueOf(entry.getValue()), AuldrawVo.class);
                auldrawVos.add(auldrawVo);
            }
            List<AuldrawVo> filterAfterAuldrawVos = auldrawVos.parallelStream().filter(auldrawVo -> (auldrawVo.getCreateTime().after(DateUtil.getCurrentDayStartTime()) && auldrawVo.getCreateTime().before(DateUtil.getCurrentDayEndTime()))).collect(Collectors.toList());
            if (filterAfterAuldrawVos.size() == 0) {
                return true;
            }
        } else if (ruleType.equals("0")) { //只能抽一次
            String lotteryAlready = stringRedisTemplate.opsForValue().get(KeyGenerator.getAuldrawLotteryAlready(deploycolId, dealerNo));
            if (org.apache.commons.lang3.StringUtils.isEmpty(lotteryAlready)) {
                return true;
            }
        }
        return false;
    }


    /**
     * 将中奖信息存储到redis
     *
     * @param auldrawVo
     * @param dealerNo
     * @param qualiSeqNo
     * @param awardsConfigVO
     */
    public void saveAwardToRedis(AuldrawVo auldrawVo, String dealerNo, String qualiSeqNo, AwardsConfigVO awardsConfigVO, QualificationVo qualificationVo) {
        String jsonString = JSON.toJSONString(auldrawVo);
        //保存中奖纪录(用于作历史记录查看)
        stringRedisTemplate.opsForHash().put(KeyGenerator.getActivityListWinningBackup(deploycolId, dealerNo), qualiSeqNo, jsonString);
        //用于同步
        stringRedisTemplate.opsForHash().put(KeyGenerator.getActivityListWinningSynch(deploycolId, dealerNo), qualiSeqNo, jsonString);
        awardsConfigVO.setQualiSeqNo(qualiSeqNo);
        if (qualificationVo != null) {
            //资格设置为已用
            qualificationVo.setQualiStatus(0);
            stringRedisTemplate.opsForHash().put(deploycolId + ":" + dealerNo, qualificationVo.getQualiSeqNo(), JSON.toJSONString(qualificationVo));
        }
    }

    /**
     * 创建中间VO
     *
     * @param awardsConfigVO
     * @param dealerNo
     * @param qualiSeqNo
     * @return
     */
    public AuldrawVo createAuldrawVo(AwardsConfigVO awardsConfigVO, String dealerNo, String qualiSeqNo) {
        AuldrawVo auldrawVo = new AuldrawVo();
        auldrawVo.setDealerNo(dealerNo);                          //卡号
        auldrawVo.setGiftCode(awardsConfigVO.getAwardsCode());    //中奖奖项Id
        auldrawVo.setAwardsName(awardsConfigVO.getAwardsName());  //奖品名称
        auldrawVo.setQualiDefNo(qualiDefNo);
        auldrawVo.setGiftQty(awardsConfigVO.getGiftQty());        //奖品数量 前端没配就null
        auldrawVo.setCreateTime(new Date());
        auldrawVo.setQualiSeqNo(qualiSeqNo);                      //序列号
        //8080是没有中奖
        if (awardsConfigVO.getAwardsCode().equals("8080")) {
            auldrawVo.setWinningPrize(0);
        } else {
            auldrawVo.setWinningPrize(1);
        }
        return auldrawVo;
    }


    /**
     * 检测是否已经登录
     *
     * @return
     */
    public Integer lotteryCheck(String dealerNo, ActivityConfigVO activityConfigVO) {
        //卡号格式合法性校验
        if (!Pattern.matches("^\\d{9}$", dealerNo) || StringUtils.isEmpty(dealerNo)) {
            return ResponseCodeConstant.ILLEGAL_DEALERNO;
        }
        // 获取登录标识
        Boolean hasKey = redisTemplate.hasKey(KeyGenerator.getLoginAlready(deploycolId, dealerNo));
//         是否已登录
        if (!hasKey) {
            return ResponseCodeConstant.UNLOGIN;
        }
        String lotteryTime = activityConfigVO.getLotteryTime();

        String[] lotteryTimeArray = lotteryTime.split(",");
        activityConfigVO.setLotteryStartDate(lotteryTimeArray[0]);
        activityConfigVO.setLotteryEndDate(lotteryTimeArray[1]);

        // 活动开始结束时间
        Date lotteryStartDate = DateUtil.parse2yyyyMMddHHmmss(activityConfigVO.getLotteryStartDate());
        Date lotteryEndDate = DateUtil.parse2yyyyMMddHHmmss(activityConfigVO.getLotteryEndDate());
        //判断活动是否开始
        if (System.currentTimeMillis() < lotteryStartDate.getTime()) {
            return ResponseCodeConstant.ACTIVITY_UNSTART;
        }
        //判断活动是否已结束
        if (System.currentTimeMillis() > lotteryEndDate.getTime()) {
            return ResponseCodeConstant.ACTIVITY_ENDED;
        }
        return null;
    }

    /**
     * 尝试获取redis锁
     * 如果仍没有获取到锁，则返回当前结果
     *
     * @param dealerNo   redis锁的名称
     * @param clientId   锁的唯一标志
     * @param retryTimes 获取锁失败时重试次数
     * @return
     */
    private boolean tryLock(String dealerNo, String clientId, Integer retryTimes) {
        boolean isLock = distributionLockService.lock(KeyGenerator.getLotteryLock(dealerNo, this.deploycolId), 5000, clientId);
        if (retryTimes-- < 0) {
            //logger.info(dealerNo + "获得锁");
            return isLock;
        }
        if (!isLock) {
            //logger.info(dealerNo + "无法获得锁");
            isLock = tryLock(dealerNo, clientId, retryTimes);
        }
        return isLock;
    }


    /**
     * @Desc 从Redis中获取资格
     */
    private Map<String, QualificationVo> getQualificationVo(String deploycolId, String dealerNo) {
        Map<String, QualificationVo> voMap = new HashMap<>();
        try {
//           //資格->   活動部署Id:卡号
            Map<Object, Object> map = stringRedisTemplate.opsForHash().entries(deploycolId + ":" + dealerNo);
            for (Map.Entry<Object, Object> entry : map.entrySet()) {
                QualificationVo qualificationVo = JSONObject.parseObject(entry.getValue().toString(), QualificationVo.class);
                voMap.put(entry.getKey().toString(), qualificationVo);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return voMap;
    }

    private Map<String, AwardsConfigVO> getAwardsList(String deploycolId) {

        Map<String, AwardsConfigVO> maps = new HashMap<>();

        String activityConfigJSON
                = stringRedisTemplate.opsForValue().get(KeyGenerator.getAwardsConfig(deploycolId));
        //logger.info("AwardsConfigVO=" + activityConfigJSON);
        List<AwardsConfigVO> list = JSONArray.parseArray(activityConfigJSON, AwardsConfigVO.class);
        for (int i = 0; i < list.size(); i++) {
            AwardsConfigVO awardsConfigVO = list.get(i);
            maps.put(awardsConfigVO.getAwardsCode(), awardsConfigVO);
        }
        return maps;
    }

    private AwardsConfigVO lotteryOperation(String deploycolId, String dealerNo) {
        AwardsConfigVO awardsConfigVO = null;
        try {
            // 奖品code
            List<String> awardsCodeList = new ArrayList<>();
            // 单个奖品中奖概率
            List<Double> awardsRateList = new ArrayList<>();
            // 从redis中获取活动对应的奖品
            Map<String, AwardsConfigVO> awardsConfigVOMap = getAwardsList(deploycolId);
            for (Map.Entry<String, AwardsConfigVO> entry : awardsConfigVOMap.entrySet()) {
                awardsCodeList.add(entry.getKey());
                awardsRateList.add(entry.getValue().getAwardsRate().doubleValue());
            }
            // 该抽奖了
            int awardsIndex = AwardsUtils.drawGift(awardsRateList);
            String awardsCode = awardsCodeList.get(awardsIndex);
            // 奖品数量不为0，则返回抽中奖品vo
            awardsConfigVO = awardsConfigVOMap.get(awardsCode);
            //logger.info(dealerNo + "get =>awardsCode=" + awardsCode);
            // 如果奖品数量为0
            int alreadyCount = stringRedisTemplate.opsForHash().increment(KeyGenerator.getAwardsAlreadyCount(deploycolId), awardsCode, 1).intValue();
            if (awardsConfigVO.getAwardsCounts() - alreadyCount < 0) {
                awardsConfigVO.setAwardsCode("8080");
                awardsConfigVO.setAwardsName("未中奖");
                stringRedisTemplate.opsForHash().increment(KeyGenerator.getAwardsAlreadyCount(deploycolId), awardsCode, -1);
                return awardsConfigVO;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return awardsConfigVO;
    }

    /**
     * 抽奖
     * @param dealerNo
     * @return
     */
    public ResponseVO lottery(String dealerNo) {
        try {
            // 从redis获取该活动的活动配置
            // 添加活动抽奖总次数标记
            String lotteryCounts =
                    stringRedisTemplate.opsForValue().get(KeyGenerator.getActivityLotteryCounts(deploycolId));
            if (null == lotteryCounts) {
                stringRedisTemplate.opsForValue().set(KeyGenerator.getActivityLotteryCounts(deploycolId), "0");
            }
            String ruleType = activityConfig.getRuleType();
            //如果是无需抽奖资格，只允许抽一次
            if (ruleType.equals("0") || ruleType.equals("2")) { //0无资格-1次机会，2无资格每天1次机会
//                // 如果该key不存在，则说明该用户未抽过奖
                if (checkCanLottery(dealerNo, ruleType)) {
                    stringRedisTemplate.opsForValue().set(KeyGenerator.getAuldrawLotteryAlready(deploycolId, dealerNo), "0");
                    AwardsConfigVO awardsConfigVO = lotteryOperation(deploycolId, dealerNo); //抽奖
                    //活动抽奖总次数加1
                    redisTemplate.opsForValue().increment(KeyGenerator.getActivityLotteryCounts(deploycolId), 1);
                    //存入Redis
                    //生成资格序列号
                    String qualiSeqNo = CodeUtils.getUUID();
                    //创建中奖信息
                    AuldrawVo auldrawVo = createAuldrawVo(awardsConfigVO, dealerNo, qualiSeqNo);
                    //保存到redis
                    saveAwardToRedis(auldrawVo, dealerNo, qualiSeqNo, awardsConfigVO, null);
                    return new ResponseVO<>().setContent(
                            VoTransferUtil.transferToVo(awardsConfigVO, new LotteryAwardVo())
                    );
                } else {
                    return new ResponseVO<>().setCode(ResponseCodeConstant.LOTTERY_TIMES_END); //已抽奖
                }
            } else if (ruleType.equals("1")) { //资格抽奖
                //logger.info("start");
                // redis锁的唯一标识
                String clientId = UUID.randomUUID().toString();
                try {
                    // 获取redis锁
                    if (!tryLock(dealerNo, clientId, 5)) {
                        return new ResponseVO<>().setCode(ResponseCodeConstant.ERROR_CODE).setContent("系统繁忙，请稍后再试!");
                    }
                    //资格抽奖
                    Map<String, QualificationVo> map = getQualificationVo(deploycolId, dealerNo);
                    if (map != null && map.size() > 0) {
                        for (Map.Entry<String, QualificationVo> entry : map.entrySet()) {
                            QualificationVo qualificationVo = entry.getValue();
                            //如果vo的资格状态为可用才能抽奖,并且抽完奖要设置为已用
                            if (qualificationVo.getQualiStatus() == QualiStateEnum.AVAILABLE.getState() || String.valueOf(qualificationVo.getQualiStatus()).equals("1")) {
                                // 进行抽奖
                                AwardsConfigVO awardsConfigVO = lotteryOperation(deploycolId, dealerNo);
                                // 活动抽奖总次数加一
                                redisTemplate.opsForValue().increment(KeyGenerator.getActivityLotteryCounts(deploycolId), 1);
                                //资格抽奖用户的已抽奖次数
                                redisTemplate.opsForValue().increment(KeyGenerator.getActivityQualiLotteryCounts(deploycolId, dealerNo), 1);
                                //创建中奖信息
                                AuldrawVo auldrawVo = createAuldrawVo(awardsConfigVO, dealerNo, qualificationVo.getQualiSeqNo());
                                //保存到redis
                                saveAwardToRedis(auldrawVo, dealerNo, qualificationVo.getQualiSeqNo(), awardsConfigVO, qualificationVo);
                                return new ResponseVO<>().setContent(
                                        VoTransferUtil.transferToVo(awardsConfigVO, new LotteryAwardVo())
                                );
                            }
                        }
                    }
                    // 没有抽奖资格了
                    return new ResponseVO<>().setCode(ResponseCodeConstant.NO_LOTTERY_QUA);
                } catch (Exception e) {
                    e.printStackTrace();
                    logger.error("", e);
                } finally {
                    distributionLockService.unlock(KeyGenerator.getLotteryLock(dealerNo, this.deploycolId), clientId);
                }
            } else {
                return new ResponseVO<>().setCode(ResponseCodeConstant.ERROR_CODE).setContent("抽奖规则配置错误！");
            }
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("", e);
        }
        return null;
    }


}
